package cn.thoughtworks.school.services;

import cn.thoughtworks.school.entities.Program;
import cn.thoughtworks.school.entities.Task;
import cn.thoughtworks.school.entities.Topic;
import cn.thoughtworks.school.entities.vo.ProgramHistoryVo;
import cn.thoughtworks.school.repositories.ProgramDiffRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.thoughtworks.school.entities.Operation.COPY;
import static cn.thoughtworks.school.entities.Operation.CREATE;

@Service
public class ProgramDiffService {
    @Autowired
    private ProgramDiffRepository programDiffRepository;
    @Autowired
    private TopicDiffService topicDiffService;
    @Autowired
    private TaskDiffService taskDiffService;
    @Autowired
    private AssignmentDiffService assignmentDiffService;
    @Autowired
    private UserCenterService userCenterService;

    public void updateProgramNotification(Map data) throws ParseException {
        Program program = Program.build(data);
        updateProgramFromId(program);
        programDiffRepository.save(program);
    }

    private void updateProgramFromId(Program program) {
        if (Objects.equals(CREATE, program.getOperation()) || Objects.equals(COPY, program.getOperation())) {
            return;
        }
        Long fromId = programDiffRepository.findFirst1ByProgramIdOrderByIdDesc(program.getProgramId()).getFromId();
        program.setFromId(fromId);
    }

    public List<Program> getProgramParentsAndChildren(Long programId) {

        List<Program> parents = programDiffRepository.getProgramParents(programId);
        List<Program> programs = programDiffRepository.getProgramChildren(parents.get(0).getProgramId());

        List<Topic> topics = topicDiffService.getLatestTopicsByProgramsId(programs.stream().map(Program::getProgramId).collect(Collectors.toList()));
        List<Task> tasks = taskDiffService.getLatestTasksByTopicsId(topics.stream().map(Topic::getTopicId).collect(Collectors.toList()));
        assignmentDiffService.fillLatestAssignmentsToTasks(tasks);
        merge(programs, topics, tasks);
        return programs;
    }

    private void merge(List<Program> programs, List<Topic> topics, List<Task> tasks) {
        topics.forEach(topic -> {
            List<Task> foundTasks = tasks.stream().filter(task -> task.getTopicId().equals(topic.getTopicId()))
                .collect(Collectors.toList());
            topic.setTasks(foundTasks);
        });
        programs.forEach(program -> {
            List<Topic> foundTopics = topics.stream().filter(topic -> topic.getProgramId().equals(program.getProgramId()))
                .collect(Collectors.toList());
            program.setTopics(foundTopics);
        });
    }

    public List<ProgramHistoryVo> getProgramHistory(Long programId) {
        List<Program> programs = programDiffRepository.findAllByProgramIdOrderByIdDesc(programId);
        List<Long> ids = programs.stream()
            .map(Program::getOperatorId)
            .distinct()
            .collect(Collectors.toList());
        List<Map> users = userCenterService.getUsersByIds(ids);
        return mergeProgramsAndUsers(programs, users);
    }

    private List<ProgramHistoryVo> mergeProgramsAndUsers(List<Program> programs, List<Map> users) {
        return programs.stream()
            .map(program -> {
                String username = users.stream()
                    .filter(user -> Objects.equals(Long.parseLong(user.get("id").toString()), program.getOperatorId()))
                    .findFirst().orElse(new HashMap())
                    .get("username")+"";

                return ProgramHistoryVo.build(program, username);
            })
            .collect(Collectors.toList());
    }
}
